package com.test.lock.CLHAndMCS;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * CLH队列自旋锁
 *
 * @author wangjie
 * @version V1.0
 * @date 2019/12/20
 */
public class CLHLock {

    public static class CLHNode {
        private volatile boolean isWaiting = true; // 默认是在等待锁
    }

    /**
     * 尾节点
     */
    private volatile CLHNode tail ;

    /**
     * 一个基于反射的工具类，它能对指定类的指定的volatile引用字段进行原子更新。(注意这个字段不能是private的)
     *
     * 通过调用AtomicReferenceFieldUpdater的静态方法newUpdater就能创建它的实例，该方法要接收三个参数：
     *     包含该字段的对象的类
     *     将被更新的对象的类
     *     将被更新的字段的名称
     */
    private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater
            . newUpdater(CLHLock.class, CLHNode .class , "tail" );


    public void lock(CLHNode currentThread) {
        CLHNode preNode = UPDATER.getAndSet( this, currentThread);

        //已有线程占用了锁，进入自旋
        if(preNode != null) {
            while(preNode.isWaiting ) {
                System.out.println(Thread.currentThread().getName()+"\t Waiting");
            }
        }
    }

    public void unlock(CLHNode currentThread) {
        // 如果队列里只有当前线程，则释放对当前线程的引用（for GC）。
        if (!UPDATER .compareAndSet(this, currentThread, null)) {
            // 还有后续线程
            // 改变状态，让后续线程结束自旋
            System.out.println(Thread.currentThread().getName()+"\t unlock");
            currentThread.isWaiting = false ;
        }
    }
    public static void main(String[] args) {

        CLHLock cLHLock = new CLHLock();

        new Thread(()->{
            CLHNode node = new CLHNode();
            cLHLock.lock(node);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cLHLock.unlock(node);
        },"AA").start();

        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            CLHNode node = new CLHNode();
            cLHLock.lock(node);
            cLHLock.unlock(node);
        },"BB").start();

    }
}
